#include "GRO_Text.h"
#include <string>


// Every child of Widget should use these in draw()
#define DRAW_BOX     painters.box.draw(area, _gui->getRenderer());
#define DRAW_FRAME   painters.frame.draw(area, _gui->getRenderer());
#define DRAW_DESIGN  painters.design.drawDesign(area, designPush, _gui->getRenderer());

namespace GRO
{
    
#define MAX_BUF 200

template<>
std::string PrimitiveObjectString<int>::getString() const
{
    char buf[MAX_BUF];
    snprintf(buf, MAX_BUF, "%d", link.get());
    return buf;
}
template<>
std::string PrimitiveObjectString<float>::getString() const
{
    char buf[MAX_BUF];
    snprintf(buf, MAX_BUF, "%f", link.get());
    return buf;
}

template class PrimitiveObjectString<int>;
template class PrimitiveObjectString<float>;

TextBuffer::TextBuffer()
{}

TextBuffer::~TextBuffer()
{}
    

std::string TextBuffer::getString()
{
    std::list<ObjectString*>::iterator e = variables.begin();
    
    std::string result;
    
    for(unsigned int i = 0; i < text.size(); i++)
    {
        if(text[i] == '#')
        {
            // Check for variable sub
            if(i+1 < text.size() && text[i+1] == '%')
            {
                if(e != variables.end())
                {
                    if(*e != NULL)
                        result += (*e)->getString();
                    e++;
                }
                i++;
            }
            else  // Real #
            {
                result += '#';
            }
        }
        else
            result += text[i];
    }
    
    return result;
}


void TextBuffer::addVariable(ObjectString* var)
{
    if(var != NULL)
        variables.push_back(var);
}





Text::Text()
    : Widget(TEXT, Dimensions(0, 0))
{}

Text::Text(const std::string& text)
    : Widget(TEXT, Dimensions(0, 0))
{
    set(text);
}

Text::~Text()
{}



void Text::refresh()
{
    text = buffer.getString();
}




void Text::draw(SDL_Rect* clipRect)
{
    if(_folded || _hidden || _gui == NULL || getFontMap() == NULL || _gui->getRenderer() == NULL)
        return;
    GRO_Font* f = getFontMap()->getDefault();
    if (f == NULL)
        return;

    
    SDL_Rect area = MakeRect(pos.x, pos.y, dims.w, dims.h);
    DRAW_BOX;

    // Draw cursor
    // Should the gui draw this?  What about controlling blink?
    /*if (editable && _gui->cursorFocus == this)
    {
        if (cursorPos > text.size())
            cursorPos = text.size();
        char str[text.size() + 1];
        strcpy(str, text.c_str());
        str[cursorPos] = '\0';
        char* c = str;
        int i = 0;
        while (i <= cursorPos)
        {
            i++;
            if (*(str + i) == '\n')
                c = str+i+1;
            else if (*(str + i) == '\0')
                break;
        }
        int x = f->getWidth(c);
        int h = f->getHeight();
        int y = f->getHeight(str) - h;
        currentRenderer->LineV(area.x + x, area.y + y, area.y + y + h - 1, cursorColor);
        currentRenderer->LineV(area.x + x - 1, area.y + y, area.y + y + h - 1, cursorColor);
        currentRenderer->LineH(area.x + x - 2, area.y + y, area.x + x + 1, cursorColor);
        currentRenderer->LineH(area.x + x - 2, area.y + y + h - 1, area.x + x + 1, cursorColor);
    }*/


    // FIXME: Parse #[0], #[1], etc. to change to the fonts in the FontMap font vector.
    int x = area.x;
    size_t lastCode = 0;
    size_t nextCode = 0;
    string t = text;
    do
    {
        nextCode = text.find_first_of("#", lastCode);
        if(nextCode != string::npos)
            t = text.substr(lastCode, nextCode - lastCode);
        else
            t = text.substr(lastCode, string::npos);
        f->draw(x, area.y, "%s", t.c_str());
        x += f->getWidth("%s", t.c_str());
        
        if(nextCode != string::npos)
        {
            int c = int(text[nextCode+1]) - 48;
            if(c >= 0 && c <= 9)
            {
                f = getFontMap()->getIndexedFont(c);
            }
            lastCode = nextCode + 2;
        }
    }
    while(nextCode != string::npos);

    DRAW_FRAME;
}

void Text::drawCenter(SDL_Rect* clipRect)
{
    if(_folded || _hidden || _gui == NULL || getFontMap() == NULL || _gui->getRenderer() == NULL)
        return;
    GRO_Font* f = getFontMap()->getDefault();
    if (f == NULL)
        return;
    
    int width = getWidth();
    // Which width should be used?  Should the width be updated whenever the text changes?
    SDL_Rect area = MakeRect(pos.x - width/2, pos.y, dims.w, dims.h);
    DRAW_BOX;

    // Draw cursor
    // Should the gui draw this?  What about controlling blink?
    /*if (editable && _gui->cursorFocus == this)
    {
        if (cursorPos > text.size())
            cursorPos = text.size();
        char str[text.size() + 1];
        strcpy(str, text.c_str());
        str[cursorPos] = '\0';
        char* c = str;
        int i = 0;
        while (i <= cursorPos)
        {
            i++;
            if (*(str + i) == '\n')
                c = str+i+1;
            else if (*(str + i) == '\0')
                break;
        }
        int x = f->getWidth(c);
        int h = f->getHeight();
        int y = f->getHeight(str) - h;
        currentRenderer->LineV(area.x + x, area.y + y, area.y + y + h - 1, cursorColor);
        currentRenderer->LineV(area.x + x - 1, area.y + y, area.y + y + h - 1, cursorColor);
        currentRenderer->LineH(area.x + x - 2, area.y + y, area.x + x + 1, cursorColor);
        currentRenderer->LineH(area.x + x - 2, area.y + y + h - 1, area.x + x + 1, cursorColor);
    }*/


    // FIXME: Parse #[0], #[1], etc. to change to the fonts in the FontMap font vector.
    int x = area.x;
    size_t lastCode = 0;
    size_t nextCode = 0;
    string t = text;
    do
    {
        nextCode = text.find_first_of("#", lastCode);
        if(nextCode != string::npos)
            t = text.substr(lastCode, nextCode - lastCode);
        else
            t = text.substr(lastCode, string::npos);
        f->draw(x, area.y, "%s", t.c_str());
        x += f->getWidth("%s", t.c_str());
        
        if(nextCode != string::npos)
        {
            int c = int(text[nextCode+1]) - 48;
            if(c >= 0 && c <= 9)
            {
                f = getFontMap()->getIndexedFont(c);
            }
            lastCode = nextCode + 2;
        }
    }
    while(nextCode != string::npos);

    DRAW_FRAME;
}


unsigned int Text::getWidth() const
{
    if(getFontMap() == NULL)
        return 0;
        
    GRO_Font* f = getFontMap()->getDefault();
    if (f == NULL)
        return 0;
        
    int x = 0;
    size_t lastCode = 0;
    size_t nextCode = 0;
    string t = text;
    do
    {
        nextCode = text.find_first_of("#", lastCode);
        if(nextCode != string::npos)
            t = text.substr(lastCode, nextCode - lastCode);
        else
            t = text.substr(lastCode, string::npos);
        x += f->getWidth("%s", t.c_str());
        
        if(nextCode != string::npos)
        {
            int c = int(text[nextCode+1]) - 48;
            if(c >= 0 && c <= 9)
            {
                f = getFontMap()->getIndexedFont(c);
            }
            lastCode = nextCode + 2;
        }
    }
    while(nextCode != string::npos);
    
    return x;
}


unsigned int Text::getHeight() const
{
    if(getFontMap() == NULL)
        return 0;
        
    GRO_Font* f = getFontMap()->getDefault();
    if (f == NULL)
        return 0;
        
    int y = 0;
    size_t lastCode = 0;
    size_t nextCode = 0;
    string t = text;
    do
    {
        nextCode = text.find_first_of("#", lastCode);
        if(nextCode != string::npos)
            t = text.substr(lastCode, nextCode - lastCode);
        else
            t = text.substr(lastCode, string::npos);
        
        int y2 = f->getHeight("%s", t.c_str());
        y = y > y2? y : y2;
        
        if(nextCode != string::npos)
        {
            int c = int(text[nextCode+1]) - 48;
            if(c >= 0 && c <= 9)
            {
                f = getFontMap()->getIndexedFont(c);
            }
            lastCode = nextCode + 2;
        }
    }
    while(nextCode != string::npos);
    
    return y;
}


void Text::updateSize()
{
    dims.w = getWidth();
    dims.h = getHeight();
}

void Text::set(const std::string& Text)
{
    text = Text;
    updateSize();
}

std::string Text::get() const
{
    return text;
}


TiXmlElement* Text::save(TiXmlElement* parent)
{
    TiXmlElement* w = new TiXmlElement("Text");
    parent->LinkEndChild(w);
    
    saveWidgetAttributes(w);
    
    w->SetAttribute("text", text.c_str());
    
    // FIXME: Save buffer...
    
    return w;
}


Beacon* getBeaconByName(Widget* w, const std::string& name);
    
void Text::connectBeacon(Widget* widget, Beacon* beacon, const std::string& fn_name, const std::vector<std::string>& args)
{
    Widget* w = widget;
    if(w == NULL)
        return;
    
    Beacon* b = beacon;
    if(b == NULL)
        return;
    
    if(fn_name == "refresh" && args.size() == 0)
    {
        b->connect<Text, void>(this, &Text::refresh);
    }
    else
        Widget::connectBeacon(widget, beacon, fn_name, args);
    return;
}



}
